using System;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
using System.Reflection;
using System.Web;
using Sedna.Core.Controller.Configurations;

namespace Sedna.Core.Controls.Support.Javascript
{
	public class ScriptObjectGenerator: IScriptObjectGenerator
	{
	    private const BindingFlags bindingFlags = BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Public;
	    
		object target;
		private bool generateChildren=true;

		public ScriptObjectGenerator(object target)
		{ 
			this.target=target;
        }

        #region Instance methods

        public ScriptObjectGenerator(object target, bool generateChildren) : this(target)
		{
			this.generateChildren = generateChildren;
		}

	    public string ToScript()
	    {
            return ScriptObjectGenerator.ToScript(this.target, this.generateChildren);
	    }
	    
		public override string ToString()
		{
            return ScriptObjectGenerator.ToScript(this.target, this.generateChildren);
        }

        #endregion

        #region Public ToScript methods

	    public static string ToScript(object target)
	    {
            return ToScript(target, true);
	    }
	    
        public static string ToScript(object target, bool generateChildren)
		{
            MethodInfo method;
            if (target == null)
            {
                return "null";
            }
            else
            {
                method = LocateToScriptMethod(target.GetType());
            }
            return (string)method.Invoke(null, new object[] { target, generateChildren });
        }

        #endregion


        #region Private ToScript methods

//        private static string NullToScript(object target)
//		{
//			return "null";	
//		}

        private static string ToScript(IScriptObjectGenerator target, bool generateChildren)
		{
			return target.ToScript();
		}

        private static string ToScript(string target, bool generateChildren)
		{
//			return String.Format("\"{0}\"", HttpUtility.HtmlAttributeEncode(target));
			return String.Format("\"{0}\"", JavascriptUtil.ToStringLiteral(target));
		}

        private static string ToScript(bool target, bool generateChildren)
		{
			return target.ToString().ToLower();
		}

        private static string ToScript(ValueType target, bool generateChildren)
		{
			Type objType=target.GetType();
			MethodInfo toString=objType.GetMethod("ToString", BindingFlags.Instance|BindingFlags.Public, null, new Type[] {typeof(CultureInfo)}, null);
			if (toString==null)
			{
				return target.ToString();
			}
			return (string)toString.Invoke(target, new object[] {CultureInfo.InvariantCulture});
		}

        private static string ToScript(IDictionary target, bool generateChildren)
		{
			string s=String.Empty;
			foreach (DictionaryEntry entry in target)
			{
				s+=String.Format("\"{0}\": {1}, ", entry.Key, ChildToScript(entry.Value, generateChildren));
			}
			return String.Format("{{{0}}}", s.TrimEnd().TrimEnd(','));
		}

        private static string ToScript(ICollection target, bool generateChildren)
		{
			string s=String.Empty;
			foreach (object obj in target)
			{
				s+=String.Format("{0}, ", ChildToScript(obj, generateChildren));	
			}
			return String.Format("[{0}]", s.TrimEnd().TrimEnd(','));	
		}

        private static string ToScript(IJavascriptClassConfiguration target, bool generateChildren)
		{
			IDictionary dict=new ListDictionary();
			dict.Add("fileName", target.FileName);
			dict.Add("dependsOn", target.DependsOn);
		    dict.Add("superClass", target.SuperClass);
			return ToScript(dict, generateChildren);
        }

        #endregion

        #region Private service methods

        private static MethodInfo LocateToScriptMethod(Type type)
		{
            MethodInfo[] methods = typeof(ScriptObjectGenerator).GetMethods(bindingFlags);
			MethodInfo bestMatchMethod=null;
			foreach (MethodInfo method in methods)
			{
				if (!method.Name.Equals("ToScript"))
				{
					continue;
				}
				ParameterInfo[] parameters=method.GetParameters();
				if (parameters.Length!=2)
				{
					continue;
				}
				if (parameters[0].ParameterType.IsAssignableFrom(type))
				{
					if (bestMatchMethod==null)
					{
						bestMatchMethod=method;
						continue;
					}
					if (bestMatchMethod.GetParameters()[0].ParameterType.IsAssignableFrom(parameters[0].ParameterType))
					{
						bestMatchMethod=method;			
					}
				}
			}
			if (bestMatchMethod!=null)
			{
				return bestMatchMethod;
			}
			throw new NotSupportedException(String.Format("Type {0} is not supported", type));
        }

        private static string ChildToScript(object child, bool generateChildren)
		{
			if (generateChildren)
			{
				return new ScriptObjectGenerator(child).ToScript();
			}
			return child==null?"null":child.ToString();
        }

        #endregion


    }
}
